﻿using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml.Linq;

namespace DotNetExtensions.Core.Helpers
{
    public class TypeSerializer
    {
        private readonly JsonSerializerSettings _settings;
        public TypeSerializer(JsonSerializerSettings settings)
        {
            _settings = settings;
        }
        private JToken GetStruct(Type type, string name, object defaultValue = null)
        {
            if (type == null) return null;

            if (TypeHelper.IsBasicType(type))
            {
                if (defaultValue != null) return JToken.FromObject(defaultValue);
                return null;
            }
            else if (TypeHelper.IsArray(type))
            {
                JObject item = new JObject();
                Type gt = Type.GetType(type.FullName.Replace("[]", string.Empty));
                if (!TypeHelper.IsBasicType(gt))
                {
                    item.Add(name, JArray.Parse("[]")); return item;
                }
                else
                {
                    return GetObjectStruct(gt, name);
                }
            }
            else if (TypeHelper.IsEnumerable(type))
            {
                Type gt = type.GetGenericArguments().FirstOrDefault();
                return new JArray()
                {
                    GetObjectStruct(gt, name)
                };
            }
            else if (type.IsClass)
            {
                return GetObjectStruct(type, name);
            }

            return null;
        }
        private JToken GetObjectStruct(Type type, string name)
        {
            if (type == null) return null;

            if (TypeHelper.IsBasicType(type))
            {
                return null;
            }
            else if (TypeHelper.IsArray(type))
            {
                JObject item = new JObject();
                Type gt = Type.GetType(type.FullName.Replace("[]", string.Empty));
                if (!TypeHelper.IsBasicType(gt))
                {
                    item.Add(name, JArray.Parse("[]")); return item;
                }
                else
                {
                    return GetObjectStruct(gt, name);
                }
            }
            else if (TypeHelper.IsEnumerable(type))
            {
                Type gt = type.GetGenericArguments().FirstOrDefault();
                return new JArray()
                {
                    GetObjectStruct(gt, name)
                };
            }
            else if (type.IsClass)
            {
                JObject structs = new JObject();
                foreach (var item in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
                {
                    if (TypeHelper.IsBasicType(item.PropertyType))
                    {
                        var val = TypeHelper.DefaultValue(item.PropertyType);
                        JToken token = null;
                        if (val != null) JToken.FromObject(val);
                        structs.Add(item.Name, token);
                    }
                    else if (TypeHelper.IsArray(item.PropertyType))
                    {
                        Type gt = Type.GetType(type.FullName.Replace("[]", string.Empty));
                        JToken subs = GetObjectStruct(gt, item.Name);
                        structs.Add(item.Name, new JArray(subs));
                    }
                    else if (TypeHelper.IsEnumerable(item.PropertyType))
                    {
                        Type gt = item.PropertyType.GetGenericArguments().FirstOrDefault();
                        JToken subs = GetObjectStruct(gt, item.Name);
                        structs.Add(item.Name, new JArray(subs));
                    }
                    else if (item.PropertyType.IsClass)
                    {
                        var subs = GetStruct(item.PropertyType, item.Name);
                        structs.Add(item.Name, subs);
                    }
                }

                return structs;
            }
            return null;
        }


        public string Serialize(Type type, string name = null, object value = null)
        {
            return Serialize(new PropertyTypeInfo(type, name, value));
        }
        public string Serialize(PropertyTypeInfo typeInfo)
        {
            return Serialize(new PropertyTypeInfo[1] { typeInfo });
        }
        public string Serialize(PropertyTypeInfo[] typeInfos)
        {
            if (typeInfos == null || typeInfos.Length == 0) return string.Empty;
            JObject jobj = new JObject();
            foreach (var item in typeInfos)
            {
                jobj.Add(item.Name, GetStruct(item.Type, item.Name));
            }
            return JsonConvert.SerializeObject(jobj, _settings);
        }
    }
}
